Skip to main content

git rebase

1. 文档

基础命令:git rebase

官方文档地址:https://git-scm.com/docs/git-rebase

功能:变基,可以对某一段线性提交历史进行编辑、删除、复制、粘贴;合理使用rebase可以使我们的提交历史干净、简洁。

提示:不要通过rebase对任何已经提交到公共仓库中的commit进行修改。

2. 基础命令

2.1 多个commit合并为一个完整的commit

命令模式:

  • git rebase -i [startcommit] [endcommit]:其中-i为打开交互面,[startcommit][endcommit]为区间选择,其中[endcommit]可以省略;
  • git rebase -i HEAD~2:合并最近两个commit

日常开发中,会存在多次commit的情况,但并不是每个commit都是需要推送到远程主机中的,git rebase命令提供了一种方式,可以将本地的多个commit合并为一个完整的commit,简化我们的提交记录。

graph LR  
A((A)) ---> B((B))
B ---> C((C))

A1((A)) ---> B1((B'))

如上图所示,当前仓库中存在commit记录A、B、C,经过git rebase需要将B、C合并为B'

以下面为例:

我们将最新两个commit合并为一个。

~/workspace/github/git-study(master ✔) git log --graph
* commit f6744cf023142db918ebbf3c30ced50bf659a35a (HEAD -> master)
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 10:33:41 2021 +0800
|
| add aa to a.txt
|
* commit 442d8f838b9681fec3849c40f4a68d77011ba390
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 10:33:25 2021 +0800
|
| add a to a.txt
|
* commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

对当前分支执行git rebase命令将多个commit合并为一个:

~/workspace/github/git-study(master ✔) git rebase -i HEAD~2
.git
1 pick 442d8f8 add a to a.txt
2 pick f6744cf add aa to a.txt
3
4 # Rebase d544582..f6744cf onto d544582 (2 commands)
5 #
6 # Commands:
7 # p, pick <commit> = use commit
8 # r, reword <commit> = use commit, but edit the commit message
9 # e, edit <commit> = use commit, but stop for amending
10 # s, squash <commit> = use commit, but meld into previous commit
11 # f, fixup <commit> = like "squash", but discard this commit's log message
12 # x, exec <command> = run command (the rest of the line) using shell
13 # b, break = stop here (continue rebase later with 'git rebase --continue')
14 # d, drop <commit> = remove commit
15 # l, label <label> = label current HEAD with a name
16 # t, reset <label> = reset HEAD to a label
17 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
18 # . create a merge commit using the original merge commit's
19 # . message (or the oneline, if no original merge commit was
20 # . specified). Use -c <commit> to reword the commit message.
21 #
22 # These lines can be re-ordered; they are executed from top to bottom.
23 #
24 # If you remove a line here THAT COMMIT WILL BE LOST.
25 #
26 # However, if you remove everything, the rebase will be aborted.
27 #
28 # Note that empty commits are commented out

修改第二个pick命令为squash命令:

.git
1 pick 442d8f8 add a to a.txt
2 squash f6744cf add aa to a.txt
3
4 # Rebase d544582..f6744cf onto d544582 (2 commands)
5 #
6 # Commands:
7 # p, pick <commit> = use commit
8 # r, reword <commit> = use commit, but edit the commit message
9 # e, edit <commit> = use commit, but stop for amending
10 # s, squash <commit> = use commit, but meld into previous commit
11 # f, fixup <commit> = like "squash", but discard this commit's log message
12 # x, exec <command> = run command (the rest of the line) using shell
13 # b, break = stop here (continue rebase later with 'git rebase --continue')
14 # d, drop <commit> = remove commit
15 # l, label <label> = label current HEAD with a name
16 # t, reset <label> = reset HEAD to a label
17 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
18 # . create a merge commit using the original merge commit's
19 # . message (or the oneline, if no original merge commit was
20 # . specified). Use -c <commit> to reword the commit message.
21 #
22 # These lines can be re-ordered; they are executed from top to bottom.
23 #
24 # If you remove a line here THAT COMMIT WILL BE LOST.
25 #
26 # However, if you remove everything, the rebase will be aborted.
27 #
28 # Note that empty commits are commented out

此时保存退出会重新打开一个vim编辑器,用来编辑新commitmessage

 1 # This is a combination of 2 commits.
2 # This is the 1st commit message:
3
4 add a to a.txt
5
6 # This is the commit message #2:
7
8 add aa to a.txt
9
10 # Please enter the commit message for your changes. Lines starting
11 # with '#' will be ignored, and an empty message aborts the commit.
12 #
13 # Date: Tue Jun 8 10:33:25 2021 +0800
14 #
15 # interactive rebase in progress; onto d544582
16 # Last commands done (2 commands done):
17 # pick 442d8f8 add a to a.txt
18 # squash f6744cf add aa to a.txt
19 # No commands remaining.
20 # You are currently rebasing branch 'master' on 'd544582'.
21 #
22 # Changes to be committed:
23 # modified: a.txt
24 #

此处可以编辑信息,也可以直接保存退出,查看当前历史记录,发现,三个记录已经变成了两个:

~/workspace/github/git-study(master ✔) git log --graph
* commit 59198b5170b043ac8fe9d64731ed59bf7628f26f (HEAD -> master)
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 10:33:25 2021 +0800
|
| add a to a.txt
|
| add aa to a.txt
|
* commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

2.2 合并分支(变基)

整体思想为:

  1. 切换到对方分支:
  2. 执行git rebase master;
  3. 切换回master,在合并对方分支。

当项目中存在多个分支,有时候我们需要进行合并又想保证git记录简约明了时可以采用这种方案:

graph LR
A((A)) --> B((B))
B --> C((C))

C --- master

A --> D((D))
D --> E((E))

E --- dev

当进行rebase合并后:

graph LR
A((A)) --> B((B))
B --> C((C))
C --> D((D'))
D --> E((E'))

E --- master
E --- dev

以下面为例,存在两个分支,他们各自都有自己的提交:

~/workspace/github/git-study(master ✔) git log master dev --graph
* commit 4914c5a1eca77f6fe3797f3f65c22e98a574f864 (HEAD -> master)
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 13:23:53 2021 +0800
|
| master add aa to a.txt
|
* commit 2969ea47fda0ca376df1b1e6e9238ad797a6ce1a
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 11:29:59 2021 +0800
|
| master add a to a.txt
|
| * commit 86322b31793c24cfb021857f8116c4fc80193e11 (dev)
| | Author: AndyWang <wangyuanwenqi@outlook.com>
| | Date: Tue Jun 8 13:24:32 2021 +0800
| |
| | dev add b to b.txt
| |
| * commit 1a31e22b6203957d91d3db0eb0181ab2215d8ff0
|/ Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 11:30:21 2021 +0800
|
| dev add b.txt
|
* commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

切换到dev分支,执行git rebase操作。这里要切换到dev分支也会是因为rebase命令的特性,rebase的含义是以谁作为基础进行变基,当然我们要以master为基础,所以要先切到dev’分支再执行rebase

~/workspace/github/git-study(dev ✔) git rebase master
First, rewinding head to replay your work on top of it...
Applying: dev add b.txt
Applying: dev add b to b.txt

切换回master分支,执行git merge操作。这里需要再执行一次merge是因为刚才的变基只存在于dev分支上,master上的commit还没有变化,需要将刚才的变基结果合并过来。

~/workspace/github/git-study(master ✔) git merge dev
Updating 4914c5a..5f757aa
Fast-forward
b.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 b.txt

查看git log记录:

~/workspace/github/git-study(master ✔) git log master dev --graph
* commit 5f757aa912b1d4df71ac79bed1bbebea063f1524 (HEAD -> master, dev)
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 13:24:32 2021 +0800
|
| dev add b to b.txt
|
* commit a57762f4b6dc96ccc8e0223f1e66696e6acd0784
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 11:30:21 2021 +0800
|
| dev add b.txt
|
* commit 4914c5a1eca77f6fe3797f3f65c22e98a574f864
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 13:23:53 2021 +0800
|
| master add aa to a.txt
|
* commit 2969ea47fda0ca376df1b1e6e9238ad797a6ce1a
| Author: AndyWang <wangyuanwenqi@outlook.com>
| Date: Tue Jun 8 11:29:59 2021 +0800
|
| master add a to a.txt
|
* commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

可以看到,dev分支上的commit都已经衍合到master分支上了,且他们的git记录是一条线,而不是merge之后的两条。

2.3 删除指定commit

当要删除某个分支中间的commit时,可以使用rebase命令。

命令模式:

  • git rebase -i [startcommit] [endcommit]:其中-i为打开交互面,[startcommit][endcommit]为区间选择,其中[endcommit]可以省略;
  • git rebase -i HEAD~4:处理最近两个commit

整体思想是:

  1. 调用git rebase命令处理最近的Ncommit
  2. 进入vim编辑器后针对需要删除的commit,使用drop命令将其删除;
  3. 保存退出执行即可

例如:

以下面git提交记录为例,删除中间的两个master字样的commit

~/workspace/github/git-study(master ✔) git log
commit 5f757aa912b1d4df71ac79bed1bbebea063f1524 (HEAD -> master, dev)
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 13:24:32 2021 +0800

dev add b to b.txt

commit a57762f4b6dc96ccc8e0223f1e66696e6acd0784
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 11:30:21 2021 +0800

dev add b.txt

commit 4914c5a1eca77f6fe3797f3f65c22e98a574f864
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 13:23:53 2021 +0800

master add aa to a.txt

commit 2969ea47fda0ca376df1b1e6e9238ad797a6ce1a
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 11:29:59 2021 +0800

master add a to a.txt

commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

执行git rebase命令,调起vim编辑框:

~/workspace/github/git-study(master ✔) git rebase -i HEAD~4
1 drop 2969ea4 master add a to a.txt
2 drop 4914c5a master add aa to a.txt
3 pick a57762f dev add b.txt
4 pick 5f757aa dev add b to b.txt
5
6 # Rebase d544582..5f757aa onto 5f757aa (4 commands)
7 #
8 # Commands:
9 # p, pick <commit> = use commit
10 # r, reword <commit> = use commit, but edit the commit message
11 # e, edit <commit> = use commit, but stop for amending
12 # s, squash <commit> = use commit, but meld into previous commit
13 # f, fixup <commit> = like "squash", but discard this commit's log message
14 # x, exec <command> = run command (the rest of the line) using shell
15 # b, break = stop here (continue rebase later with 'git rebase --continue')
16 # d, drop <commit> = remove commit
17 # l, label <label> = label current HEAD with a name
18 # t, reset <label> = reset HEAD to a label
19 # m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
20 # . create a merge commit using the original merge commit's
21 # . message (or the oneline, if no original merge commit was
22 # . specified). Use -c <commit> to reword the commit message.
23 #
24 # These lines can be re-ordered; they are executed from top to bottom.
25 #
26 # If you remove a line here THAT COMMIT WILL BE LOST.
27 #
28 # However, if you remove everything, the rebase will be aborted.
29 #
30 # Note that empty commits are commented out

将两个有master字样的提交记录标记为命令drop,保存退出执行,查看删除之后的记录:

~/workspace/github/git-study(master ✔) git log
commit 74fdbf785410310f220f6c2f34afaef6a30e1387 (HEAD -> master)
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 13:24:32 2021 +0800

dev add b to b.txt

commit 0ff05c564ef803361804b6d3655328665c062aec
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 11:30:21 2021 +0800

dev add b.txt

commit d54458266ca9cc99b1f020122ade3486c6e158ab
Author: AndyWang <wangyuanwenqi@outlook.com>
Date: Tue Jun 8 10:33:00 2021 +0800

init a

可以看完,已经完美删了了中间的两条历史记录。

2.4 --interactive && -i

以交互方式进行rebase